package jvlite.ioc;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.Callable;

import jvlite.aop.DefaultAdvice;
import jvlite.aop.IAdvice;
import jvlite.aop.ProxyHandler;
import jvlite.core.JLiteContext;
import jvlite.mvc.annotaion.Insert;
import jvlite.mvc.annotaion.PointCut;
import jvlite.util.StrUtil;

public class Injector implements Callable<Boolean> {

	public static void doInject() {
		// deal with dependent inject while iterate all class
		for (Class<?> clz : JLiteContext.clazzSet) {
			matchInject(clz);
		}
	}

	public static void matchInject(Class<?> cc) {
		Map<String, Object> DIMap = BeanFactory.getBeanMap();
		String clzName = cc.getName();
		Field[] fields = cc.getDeclaredFields();
		for (Field field : fields) {
			// consider performance !!!!!
			field.setAccessible(true);
			if (field.isAnnotationPresent(Insert.class)) {
				Insert injectObj = field.getAnnotation(Insert.class);
				String value = injectObj.value();
				// search other class for inject
				for (String fullName : DIMap.keySet()) {
					String sName = fullName
							.substring(fullName.lastIndexOf(".") + 1);
					if (sName.equals(StrUtil.firstUp(value))) {
						Object toInjectObj = DIMap.get(fullName);
						Class<? extends Object> tjClass = toInjectObj
								.getClass();
						// recursive inject
						if (null != tjClass) {
							matchInject(tjClass);
						}
						// field.setAccessible(true);
						try {
							if (toInjectObj instanceof Proxy) {
								field.set(DIMap.get(clzName), toInjectObj);
							} else {
								field.set(DIMap.get(clzName),
										getProxyByObj(toInjectObj));
							}
						} catch (IllegalArgumentException e) {
							e.printStackTrace();
						} catch (IllegalAccessException e) {
							e.printStackTrace();
						} catch (Throwable e) {
							e.printStackTrace();
						}
					}
				}
			}
		}
	}

	public static Object getProxyByObj(Object target) {
		Map<String, Object> DIMap = BeanFactory.getDIMap();
		Object obj = null;
		Class<?> cc = null;
		cc = target.getClass();
		ProxyHandler proxyHandler = new ProxyHandler();
		// deal with point cut
		if (cc.isAnnotationPresent(PointCut.class)) {
			PointCut pointCut = cc.getAnnotation(PointCut.class);
			String adviceName = pointCut.advice();
			IAdvice advice = null;
			if (adviceName == null || "".equals(adviceName)) {
				advice = new DefaultAdvice();// default advice
			} else {
				for (String cName : DIMap.keySet()) {// search_for_advice
					String sName = cName.substring(cName.lastIndexOf(".") + 1);// name_without_path
					if (sName.equals(StrUtil.firstUp(adviceName))) {
						try {
							advice = (IAdvice) DIMap.get(cName);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
			}
			obj = proxyHandler.factory(target, advice);
		} else {
			obj = target;
		}
		return obj;
	}

	@Override
	public Boolean call() throws Exception {
		doInject();
		return true;
	}

}